//
// Created by martin on 3/22/22.
//

#include "FtpContext.h"
#include "muduo/net/Buffer.h"
#include "muduo/base/Logging.h"

using namespace ftp;
using namespace muduo;
using namespace muduo::net;

FtpContext::FtpContext(EventLoop* loop, const TcpConnectionPtr& ctrlConn, const WeakIdleConnectionEntryPtr& weakEntryPtr, const UserDao& ftpUserDao_)
	: loop_(loop),
	controlConnWeakPtr_(ctrlConn),
	weakEntryPtr_(weakEntryPtr),
	session_(new FtpSession(ctrlConn, ftpUserDao_)),
	state_(kExpectRequestLine),
	numErrors_(0)
{
	LOG_INFO << "Create new FtpContext object@" << this;
}

FtpContext::~FtpContext()
{
	LOG_INFO << "Destroy FtpContext object@" << this;
}


/**
 * 解析输入缓冲, 将解析出的各字段结果存放到FtpRequest对象中.
 *
 * 一条完整ftp请求命令, 以"\r\n"结尾, 形如
 *   1) "PWD\r\n", command name = "PWD"
 *
 *   2) "USER martin\r\n", command name = "USER", command parameter = "martin"
 *
 * @param buffer 连接的输入缓存
 * @param receiveTime 接收到消息的时间
 *
 * @return 收到1条完整ftp请求命令, 并成功解析时, 会将解析结果保存到成员request_
 */
bool FtpContext::parseRequest(Buffer* buffer, Timestamp receiveTime)
{
	bool ok = false;
	bool hasMore = true;

	while (hasMore)
	{
		switch (state_)
		{
		case kExpectRequestLine:
		{
			const char* crlf = buffer->findCRLF();
			if (crlf)
			{ // "\r\n"结尾字符串
				ok = doRequestLine(buffer->peek(), crlf);
				if (ok)
				{
					request_.setReceiveTime(receiveTime);
					buffer->retrieveUntil(crlf + 2);
					state_ = kGotAll;
				}
			}
			hasMore = false;
		}
		break;
#if 0
		case kExpectCommand:
		{
		}
		break;
		case kExpectParam:
		{
			state_ = kGotAll;
		}
		break;
#endif
		default:
			hasMore = false;
		}
	}

	if (!ok) { // 错误计数
		++numErrors_;
	}
	return ok;
}

/**
 * 处理去掉 "\r\n"后的请求字符串. FTP请求命令有2类: 带参数, 不带参数.
 * 带参数的, 命令和参数之间用空格分隔; 不带参数的, 只有一个单独的命令．
 * @param begin 字符串起始位置
 * @param end　字符串终止位置, 指向NUL byte
 * @return
 */
bool FtpContext::doRequestLine(const char* begin, const char* end)
{
	bool succeed = true;
	const char* start = begin;
	const char* space = std::find(start, end, ' ');
	if (space != end)
	{
		request_.setCommandName(start, space);
		start = space + 1;
		while (start != end && *start == ' ') // 过滤命令和参数之间的空格<SP>
		{
			++start;
		}
		if (start != end)
		{
			request_.setCommandParam(start, end);
		}
	}
	else
	{
		request_.setCommandName(start, end);
	}
	return succeed;
}

void FtpContext::startFtpSession()
{
	session_->start();
}

void FtpContext::handleFtpCommand()
{
	session_->handleFtpCommand(request_);
}
